package gmap

/*
gmap.Map is a thread-safety replacement for go's native map structure. The gmap.Map internally use
map to ensure thread safety. To solve the concurrent issue of a single lock, we refer to memcached's
internal implementation of using locks of fine granularity to achieve higher concurrency.
*/

import "sync"

type Map struct {
	lockSize uint
	locks    []*sync.RWMutex
	maps     []map[string]interface{}
	hashAlg  HashAlgorithm
}

type HashAlgorithm func(string) uint

func hash33(key string) uint {
	var hash uint
	hash = 0
	for _, rune := range key {
		c := uint(rune)
		hash += (hash<<5 + hash) + c
	}
	return hash
}

func NewMap(lockSize uint) *Map {
	gmap := &Map{
		lockSize: lockSize,
		locks:    make([]*sync.RWMutex, lockSize),
		maps:     make([]map[string]interface{}, lockSize),
		hashAlg:  HashAlgorithm(hash33),
	}
	for i, _ := range gmap.maps {
		gmap.locks[i] = new(sync.RWMutex)
		gmap.maps[i] = make(map[string]interface{})
	}
	return gmap
}

func (m *Map) Bucket(i int) map[string]interface{} {
	if 0 <= i && i <= int(m.lockSize-1) {
		return m.maps[i]
	}
	return nil
}

func (m *Map) BucketLock(i int) *sync.RWMutex {
	if 0 <= i && i <= int(m.lockSize-1) {
		return m.locks[i]
	}
	return nil
}

func (m *Map) Get(key string) (itr interface{}, ok bool) {
	section := m.getSection(key)
	lock := m.locks[section]
	_map := m.maps[section]
	lock.RLock()
	defer lock.RUnlock()
	itr, ok = _map[key]
	return
}

func (m *Map) Set(key string, value interface{}) {
	section := m.getSection(key)
	lock := m.locks[section]
	_map := m.maps[section]
	lock.Lock()
	defer lock.Unlock()
	_map[key] = value
}

func (m *Map) Delete(key string) {
	section := m.getSection(key)
	lock := m.locks[section]
	_map := m.maps[section]
	lock.Lock()
	defer lock.Unlock()
	delete(_map, key)
}

func (m *Map) getSection(key string) int {
	return int(m.hashAlg(key) % m.lockSize)
}
